package cn.assumejson.springframework.beans.factory.xml;

import cn.assumejson.springframework.beans.PropertyValue;
import cn.assumejson.springframework.beans.factory.BeansException;
import cn.assumejson.springframework.beans.factory.config.RuntimeBeanNameReference;
import cn.assumejson.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import cn.assumejson.springframework.beans.factory.support.BeanDefinitionRegistry;
import cn.assumejson.springframework.beans.factory.support.RootBeanDefinition;
import cn.assumejson.springframework.context.context.annotation.ClassPathBeanDefinitionScanner;
import cn.assumejson.springframework.core.io.Resource;
import cn.assumejson.springframework.core.io.ResourceLoader;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.Document;


import java.beans.Introspector;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import static cn.assumejson.springframework.util.XmlUtils.*;

/**
 * xml文件读取器
 *
 * @author Mr. Li
 * @version 1.0
 * @since 1.0 2021/11/30 13:07
 */
@Slf4j
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        try (InputStream is = resource.getInputStream()) {
            doLoadBeanDefinitions(is);
        } catch (IOException | DocumentException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void loadBeanDefinitions(Resource... resources) throws BeansException {
        for (Resource resource : resources) {
            loadBeanDefinitions(resource);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }

    /**
     * 读取XML配置文件
     *
     * @param is 输入流
     */
    private void doLoadBeanDefinitions(InputStream is) throws DocumentException, ClassNotFoundException {
        log.info("running read xml is starting");
        SAXReader reader = new SAXReader();
        Document document = reader.read(is);
        Element root = document.getRootElement();

        // 判断是否开启了 注解扫描 <context:component-scan base-package="cn.bugstack.springframework.test.bean"/>
        Element componentScan = root.element("component-scan");
        if (null != componentScan) {
            String scanPath = componentScan.attributeValue("base-package");
            if (StrUtil.isEmpty(scanPath)) {
                throw new BeansException("The value of base-package attribute can not be empty or null");
            }
            scanPackage(scanPath);
        }

        List<Element> beanList = root.elements(BEAN);
        for (Element element : beanList) {

            // 解析标签
            String id = element.attributeValue(BEAN_ID);
            String name = element.attributeValue(BEAN_NAME);
            String className = element.attributeValue(BEAN_CLASS);
            // 初始化 和销毁方法
            String initMethod = element.attributeValue(BEAN_INIT_METHOD);
            String destroyMethod = element.attributeValue(BEAN_DESTROY_METHOD);
            //scope
            String scope = element.attributeValue(BEAN_SCOPE);

            // 获取 Class，方便获取类中的名称
            Class<?> clazz = Class.forName(className);
            // 优先级 id > name
            String beanName = StrUtil.isNotEmpty(id) ? id : name;
            if (StrUtil.isEmpty(beanName)) {
                if (StrUtil.isEmpty(name)) {
                    beanName = Introspector.decapitalize(clazz.getSimpleName());
                } else {
                    beanName = Introspector.decapitalize(name);
                }
            }

            // 定义Bean
            RootBeanDefinition bd = new RootBeanDefinition(clazz);
            bd.setInitMethodName(initMethod);
            bd.setDestroyMethodName(destroyMethod);
            if (StrUtil.isNotBlank(scope)) {
                bd.setScope(scope);
            }

            List<Element> propertyList = element.elements(BEAN_PROPERTY);

            for (Element property : propertyList) {

                // 解析标签：property
                String attrName = property.attributeValue(BEAN_PROPERTY_NAME);
                String attrValue = property.attributeValue(BEAN_PROPERTY_VALUE);
                String attrRef = property.attributeValue(BEAN_PROPERTY_REF);
                // 获取属性值：引入对象、值对象
                Object value = StrUtil.isNotBlank(attrRef) ? new RuntimeBeanNameReference(attrRef) : attrValue;
                // 创建属性信息
                PropertyValue propertyValue = new PropertyValue(attrName, value);
                bd.getPropertyValues().addPropertyValue(propertyValue);
            }
            if (getRegistry().containsBeanDefinition(beanName)) {
                throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
            }
            // 注册 BeanDefinition
            getRegistry().registerBeanDefinition(beanName, bd);
        }
        log.info("Finishing read xml is ending");
    }

    private void scanPackage(String scanPath) {
        String[] basePackages = StrUtil.splitToArray(scanPath, ',');
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry());
        scanner.scan(basePackages);
    }
}